//
// Created by HP on 2025/11/12.
//
#include "../include/redis.h"

RdConnector* RdConnector::instance_ = nullptr;

/**
 * @brief 连接到Redis服务器
 * @return 连接成功返回true，失败返回false
 *
 * 该函数执行以下操作：
 * 1. 检查是否已经存在其他RdConnector实例
 * 2. 如果已存在连接，则先断开
 * 3. 将端口号字符串转换为整数
 * 4. 尝试连接到Redis服务器
 * 5. 如果配置了密码，进行身份验证
 * 6. 如果指定了非0数据库，切换到指定数据库
 * 7. 如果是第一个实例，则设置instance_指针
 */
bool RdConnector::connect() {
    // 检查是否已经存在其他RdConnector实例
    if (instance_ != nullptr && instance_ != this) {
        std::cerr << "Only one instance of RdConnector is allowed." << std::endl;
        return false;
    }

    // 如果已存在连接，则先断开
    if (context != nullptr) {
        redisFree(context);
        context = nullptr;
    }
    // 将端口号字符串转换为整数
    int port ;
    try {
        port = std::stoi(port_);
    } catch (...) {
        std::cerr << "端口格式错误：" << port << std::endl;
        return false;
    }
    // 尝试连接到Redis服务器
    context = redisConnect(host_.c_str(), port);
    if (context == nullptr || context->err) {
        std::cerr << "连接失败：" << (context ? context->errstr : "内存分配失败") << std::endl;
        return false;
    }
    // 如果配置了密码，进行身份验证
    if (!password_.empty()) {
        auto* reply = (redisReply*)redisCommand(context, "AUTH %s", password_.c_str());
        if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
            std::cerr << "密码验证失败：" << (reply ? reply->str : context->errstr) << std::endl;
            redisFree(context);
            context = nullptr;
            freeReplyObject(reply);
            return false;
        }
        freeReplyObject(reply);  // 释放命令结果
    }
    // 如果指定了非0数据库，切换到指定数据库
    if(db!=0){
        auto* reply = (redisReply*)redisCommand(context, "SELECT %d", db);
        if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
            std::cerr << "切换数据库失败：" << (reply ? reply->str : context->errstr) << std::endl;
            redisFree(context);
            context = nullptr;
            freeReplyObject(reply);
            return false;
        }
        freeReplyObject(reply);  // 释放命令结果
    }

    // 如果是第一个实例，则设置instance_指针
    if (instance_ == nullptr) {
        instance_ = this;
    }

    return true;
}

/**
 * 从Redis中获取指定键的值
 * @param key 要获取的键名
 * @return 返回键对应的值，如果键不存在或发生错误则返回空字符串
 */
std::string RdConnector::get(const std::string& key) {
    // 检查Redis连接上下文是否有效
    if (context == nullptr){
        std::cerr<<"未连接"<<std::endl;
        return "";
    }
    // 执行Redis GET命令获取键值
    auto* reply = (redisReply*)redisCommand(context, "GET %s", key.c_str());
    // 检查命令执行是否成功
    if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
        std::cerr << "获取失败：" << (reply ? reply->str : context->errstr) << std::endl;
        freeReplyObject(reply);
        return "";
    }
    std::string result;
    // 根据返回类型处理结果
    if (reply->type == REDIS_REPLY_STRING) {
        result = reply->str;  // 键存在，返回值
    } else if (reply->type == REDIS_REPLY_NIL) {
        result = "";  // 键不存在，返回空
    } else {
        // 处理其他异常情况
        std::cerr << "GET 命令返回异常（类型：" << reply->type << "）" << std::endl;
        result = "";
    }
    // 释放Redis回复对象
    freeReplyObject(reply);
    return result;
}

/**
 * @brief 设置Redis键值对
 *
 * @param key 要设置的键
 * @param value 要设置的值
 * @return bool 操作是否成功
 *
 * 该函数用于在Redis数据库中设置键值对。首先检查Redis连接是否有效，
 * 然后执行SET命令，最后检查命令执行结果并返回操作状态。
 */
bool RdConnector::set(const std::string& key, const std::string& value) {
    // 检查Redis上下文是否初始化，即是否已连接到Redis服务器
    if (context == nullptr){
        std::cerr<<"未连接"<<std::endl;
        return false;
    }
    // 执行Redis SET命令，将键值对存入Redis
    auto* reply = (redisReply*)redisCommand(context, "SET %s %s", key.c_str(), value.c_str());
    // 检查Redis命令是否执行成功
    if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
        std::cerr << "命令执行失败：" << (reply ? reply->str : context->errstr) << std::endl;
        freeReplyObject(reply);
        return false;
    }
    // 检查Redis返回的状态是否为"OK"，确认SET操作是否成功
    bool success = (reply->type == REDIS_REPLY_STATUS && std::string(reply->str) == "OK");
    if (!success) {
        std::cerr << "SET 命令失败：" << (reply->str ? reply->str : "未知错误") << std::endl;
    }
    // 释放Redis回复对象，防止内存泄漏
    freeReplyObject(reply);
    return success;
}

/**
 * @brief 设置Redis键值对并指定过期时间
 *
 * @param key 要设置的键
 * @param value 要设置的值
 * @param expireSeconds 过期时间（秒）
 * @return bool 操作是否成功
 *
 * 该函数用于在Redis数据库中设置键值对，并设置过期时间。首先检查Redis连接是否有效，
 * 然后执行SETEX命令（SET with EXpire），最后检查命令执行结果并返回操作状态。
 */
bool RdConnector::set(const std::string& key, const std::string& value, int expireSeconds) {
    // 检查Redis上下文是否初始化，即是否已连接到Redis服务器
    if (context == nullptr){
        std::cerr<<"未连接"<<std::endl;
        return false;
    }
    // 检查过期时间是否有效
    if (expireSeconds <= 0) {
        std::cerr << "过期时间必须大于0" << std::endl;
        return false;
    }
    // 执行Redis SETEX命令，将键值对存入Redis并设置过期时间
    auto* reply = (redisReply*)redisCommand(context, "SETEX %s %d %s", key.c_str(), expireSeconds, value.c_str());
    // 检查Redis命令是否执行成功
    if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
        std::cerr << "命令执行失败：" << (reply ? reply->str : context->errstr) << std::endl;
        freeReplyObject(reply);
        return false;
    }
    // 检查Redis返回的状态是否为"OK"，确认SETEX操作是否成功
    bool success = (reply->type == REDIS_REPLY_STATUS && std::string(reply->str) == "OK");
    if (!success) {
        std::cerr << "SETEX 命令失败：" << (reply->str ? reply->str : "未知错误") << std::endl;
    }
    // 释放Redis回复对象，防止内存泄漏
    freeReplyObject(reply);
    return success;
}

/**
 * @brief 检查Redis中指定键是否存在
 *
 * @param key 要检查的键名
 * @return bool 键存在返回true，不存在或发生错误返回false
 *
 * 该函数用于检查Redis数据库中是否存在指定的键。首先检查Redis连接是否有效，
 * 然后执行EXISTS命令，最后根据返回结果判断键是否存在。
 */
bool RdConnector::exists(const std::string& key) {
    // 检查Redis上下文是否初始化，即是否已连接到Redis服务器
    if (context == nullptr){
        std::cerr<<"未连接"<<std::endl;
        return false;
    }
    // 执行Redis EXISTS命令检查键是否存在
    auto* reply = (redisReply*)redisCommand(context, "EXISTS %s", key.c_str());
    // 检查Redis命令是否执行成功
    if (reply == nullptr || reply->type == REDIS_REPLY_ERROR) {
        std::cerr << "命令执行失败：" << (reply ? reply->str : context->errstr) << std::endl;
        freeReplyObject(reply);
        return false;
    }
    // EXISTS命令返回整数：1表示键存在，0表示键不存在
    bool keyExists = false;
    if (reply->type == REDIS_REPLY_INTEGER) {
        keyExists = (reply->integer == 1);
    } else {
        std::cerr << "EXISTS 命令返回异常（类型：" << reply->type << "）" << std::endl;
    }
    // 释放Redis回复对象，防止内存泄漏
    freeReplyObject(reply);
    return keyExists;
}

RdConnector *RdConnector::getInstance() {
    return instance_;
}

std::string RdConnector::getError() {
    if (context == nullptr) {
        return "未初始化连接";
    }
    return context->errstr;
}